package json2xml;

// using dom (document object model)

/*
 translate from
 {
  "description" : "An imaginary server config file",
  "logs" : {"level":"verbose", "dir":"/var/log"},
  "host" : "antlr.org",
  "admin": ["parrt", "tombu"],
  "aliases": []
}
to

<description>An imaginary server config file</description>
<logs>
<level>verbose</level>
<dir>/var/log</dir>
</logs>
<host>antlr.org</host>
<admin>
<element>parrt</element>
<element>tombu</element>
</admin>
<aliases></aliases>

 */

import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.misc.Utils;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeProperty;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

public class JSON2XMLDom {
    // what?
    // static {
    //     try {
    //         DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
    //         DocumentBuilder documentBuilder = builderFactory.newDocumentBuilder();
    //         Document document = documentBuilder.newDocument();
    //         Element root = document.createElement("object");
    //         document.appendChild(root);
    //         root.appendChild(document.createTextNode("foo"));
    //
    //         TransformerFactory transformerFactory = TransformerFactory.newInstance();
    //         Transformer transformer = transformerFactory.newTransformer();
    //         DOMSource domSource = new DOMSource(document);
    //         StreamResult result = new StreamResult(System.out);
    //         transformer.transform(domSource, result);
    //     } catch (Exception e) {
    //         e.printStackTrace();
    //     }
    // }

    public static class Node {

    }

    public static class Element extends Node {
        String name;
        List<Node> children = new ArrayList<>();
        public Element(String name) {
            this.name = name;
        }

        public String toString() {
            String content = Utils.join(children.iterator(), "");
            if (name == null) {
                return content;
            }
            return XMLEmitter.tag(name, content);
        }
    }

    // Text node
    public static class TextNode extends Node {
        String content;
        public TextNode(String content) {
            this.content = content;
        }

        public String toString() {
            return content;
        }
    }

    public static class XMLEmitter extends JSONBaseListener {

        ParseTreeProperty<Node> nodes = new ParseTreeProperty<>();

        public void exitJson(JSONParser.JsonContext context) {
            nodes.put(context, nodes.get(context.getChild(0)));
        }

        public void exitAnObject(JSONParser.AnObjectContext context) {
            Element obj = new Element(null);
            for (JSONParser.PairContext pairContext : context.pair()) {
                obj.children.add(nodes.get(pairContext));
            }
            nodes.put(context, obj);
        }

        public void exitArrayOfValues(JSONParser.ArrayOfValuesContext context) {
            Element array = new Element(null);
            for (JSONParser.ValueContext valueContext : context.value()) {
                array.children.add(nodes.get(valueContext));
            }
            nodes.put(context, array);
        }

        public void exitPair(JSONParser.PairContext context) {
            String name = stripQuotes(context.STRING().getText());
            JSONParser.ValueContext valueContext = context.value();
            Element p = new Element(name);
            Node value = nodes.get(valueContext);
            if (value != null) {
                if (value instanceof Element && ((Element) value).name == null) {
                    // if null content, must be object or array
                    p.children.addAll(((Element)value).children);
                } else {
                    p.children.add(value);
                }
            }
            nodes.put(context, p);
        }

        public void exitObjectValue(JSONParser.ObjectValueContext context) {
            nodes.put(context, nodes.get(context.object()));
        }

        public void exitArrayValue(JSONParser.ArrayValueContext context) {
            JSONParser.ArrayContext arrayContext = context.array();
            nodes.put(context, nodes.get(arrayContext));
        }

        public void exitString(JSONParser.StringContext context) {
            TextNode text = new TextNode(stripQuotes(context.start.getText()));
            if (context.parent instanceof JSONParser.ArrayContext) {
                Element implicitTag = new Element("element");
                implicitTag.children.add(new TextNode(context.start.getText()));
                nodes.put(context, implicitTag);
            } else {
                nodes.put(context, text);
            }
        }

        public void exitAtom(JSONParser.AtomContext context) {
            TextNode textNode = new TextNode(context.start.getText());
            if (context.parent instanceof JSONParser.ArrayContext) {
                Element implicitTag = new Element("element");
                implicitTag.children.add(new TextNode(context.start.getText()));
                nodes.put(context, implicitTag);
            } else {
                nodes.put(context, textNode);
            }
        }

        // as simple as possible no simpler
        public static String tag(String name, String content) {
            return '<' + name + '>' +
                    content +
                    "</" + name + '>';
        }

        public static String stripQuotes(String s) {
            if (s == null || s.charAt(0) != '"') {
                return s;
            }
            return s.substring(1, s.length()-1);
        }
    }

    public static void main(String[] args) throws IOException {
        String inputFile = null;
        if (args.length > 0) {
            inputFile = args[0];
        }
        InputStream is = System.in;
        if (inputFile != null) {
            is = new FileInputStream(inputFile);
        }
        CharStream input = CharStreams.fromStream(is);
        JSONLexer lexer = new JSONLexer(input);
        CommonTokenStream tokenStream = new CommonTokenStream(lexer);
        JSONParser parser = new JSONParser(tokenStream);
        parser.setBuildParseTree(true);
        ParseTree tree = parser.json();

        ParseTreeWalker walker = new ParseTreeWalker();
        XMLEmitter converter = new XMLEmitter();
        walker.walk(converter, tree);
        System.out.println(converter.nodes.get(tree));
    }
}
